﻿/*
 *      CentroVentasCore32  - CVCore32.dll
 *      
 *      Desarrollado por:
 *                      Francisco Acevedo.
 *                      David Avila.
 *                      José Pérez.
 *                      
 *      Todos los derechos reservados.
 */

/*
 *      CLASE: SingletonApp   
 * Notas de versión:
 * 
 * [04-01-2012]
 *      -   Creacion de la clase. [http://danielvaughan.orpius.com/post/Enforcing-Single-Instance-WPF-Applications.aspx]
 */


using System;
using System.Collections.Generic;
using System.Threading;
using System.IO.MemoryMappedFiles;
using System.IO;
using System.Diagnostics;


namespace CVCore32.Class.Core
{
    public sealed class SingletonApp
    {
        readonly Action<IEnumerable<string>> processArgsFunc;
        readonly string applicationId;
        Thread thread;
        string argDelimiter = "_;;_";

        public string ArgDelimeter
        {
            get 
            {
                return argDelimiter;
            }
            set
            {
                argDelimiter = value;
            }
        }

        public SingletonApp(Action<IEnumerable<string>> processArgsFunc, string applicationId = "DisciplesRock")
        {
            if (processArgsFunc == null)
            {
                throw new ArgumentNullException("processArgsFunc");
            }
            this.processArgsFunc = processArgsFunc;
            this.applicationId = applicationId;
        }

        public bool ShouldApplicationExit()
        {
            bool createdNew;
            string argsWaitHandleName = "ArgsWaitHandle_" + applicationId;
            string memoryFileName = "ArgFile_" + applicationId;
            EventWaitHandle argsWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, argsWaitHandleName, out createdNew);
            GC.KeepAlive(argsWaitHandle);
            if (createdNew)
            {
                /* This is the main, or singleton application. 
                 * * A thread is created to service the MemoryMappedFile. 
                 * * We repeatedly examine this file each time the argsWaitHandle 
                 * * is Set by a non-singleton application instance. */
                thread = new Thread(() =>
                {
                    try
                    {
                        using (MemoryMappedFile file = MemoryMappedFile.CreateOrOpen(memoryFileName, 10000))
                        {
                            while (true)
                            {
                                argsWaitHandle.WaitOne();
                                using (MemoryMappedViewStream stream = file.CreateViewStream())
                                {
                                    var reader = new BinaryReader(stream);
                                    string args;
                                    try
                                    {
                                        args = reader.ReadString();
                                    }
                                    catch (Exception ex)
                                    {
                                        Debug.WriteLine("Unable to retrieve string. " + ex);
                                        continue;
                                    }
                                    string[] argsSplit = args.Split(new string[] { argDelimiter }, StringSplitOptions.RemoveEmptyEntries);
                                    processArgsFunc(argsSplit);                                
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine("Unable to monitor memory file. " + ex);
                    }
                });
                
                thread.IsBackground = true;
                thread.Start();
            }
            else
            {
                /* Non singleton application instance. 
                 * * Should exit, after passing command line args to singleton process, 
                 * * via the MemoryMappedFile. */
                using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(memoryFileName))
                {
                    using (MemoryMappedViewStream stream = mmf.CreateViewStream())
                    {
                        var writer = new BinaryWriter(stream);
                        string[] args = Environment.GetCommandLineArgs();
                        string joined = string.Join(argDelimiter, args);
                        writer.Write(joined);
                    }
                }
                argsWaitHandle.Set();
            }
            return !createdNew;
        }
    }
}
